package com.wh1200.mybatis.xmlless

import com.wh1200.mybatis.xmlless.util.TypeUtil
import org.apache.ibatis.io.Resources
import org.slf4j.LoggerFactory
import org.springframework.core.io.Resource
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
import org.springframework.core.io.support.ResourcePatternResolver
import org.springframework.core.type.classreading.CachingMetadataReaderFactory
import org.springframework.util.ClassUtils
import org.springframework.util.StringUtils
import java.io.IOException

/**
 *
 * @author wuhao
 * @date 2024/1/16 21:47
 * @since v0.0.0
 * @version 1.0
 */

object TypeRegistry {

  private val LOGGER = LoggerFactory.getLogger(TypeUtil::class.java)


  fun init(packagePatterns: String, assignableType: Class<*>? = null) {
    scanClasses(packagePatterns, assignableType)
        .filter { !it.name.contains("\$") }
        .forEach {
          TypeUtil.register(it)
        }
  }

  @Throws(IOException::class)
  private fun scanClasses(packagePatterns: String, assignableType: Class<*>?): Set<Class<*>> {
    val classes: MutableSet<Class<*>> = HashSet()
    val packagePatternArray = StringUtils.tokenizeToStringArray(
        packagePatterns, ",; \t\n"
    )
    for (packagePattern: String in packagePatternArray) {
      val resources = PathMatchingResourcePatternResolver().getResources(
          ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
              + ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class"
      )
      for (resource: Resource in resources) {
        try {
          val classMetadata = CachingMetadataReaderFactory().getMetadataReader(resource).classMetadata
          val clazz = Resources.classForName(classMetadata.className)
          if (assignableType == null || assignableType.isAssignableFrom(clazz)) {
            classes.add(clazz)
          }
        } catch (e: Throwable) {
          LOGGER.warn("Cannot load the '$resource'. Cause by $e")
        }
      }
    }
    return classes
  }


}
